Part I
Core MFC

In This Part

  The MFC Architecture 7
  MFC Dialogs, Controls, and Data Interaction 49
  The Windows Common Controls 79
  Painting, Device Contexts, Bitmaps, and Fonts 147
  Custom Control Development 213
  The MFC Application Object, Message Routing, and Idle Processing 241

Chapter 1
The MFC Architecture

by Bill Heyman

In This Chapter

  A Brief History of MFC 8
  The MFC Class Hierarchy 13

The Microsoft Foundation Classes (MFC) allow you to develop C++ GUI applications for Windows using its rich set of classes. This chapter discusses the evolution of MFC and the fundamental classes used in almost every MFC-based application.

A Brief History of MFC

Since its beginnings in 1987, Windows has introduced legions of traditional DOS programmers to a new way of programming: a device-independent, event-driven model. Although the Windows API has grown to add much new functionality, it still retains the basic functions that existed in the early versions of Windows (such as Windows/286 and Windows/386).

In the late 1980s, BASIC, 8088 assembler, and Pascal were the lingua francae for DOS software development. At this time, the C language was starting to grow beyond its UNIX roots and become a high-performance, systems development language on other platforms. Microsoft’s choice of using C (combined with 8088 assembler) for the development of Windows allowed C to gain a foothold among the PC developers.

The original Windows API (now sometimes called Win16) catered to using a C development environment. The American National Standards Institute (ANSI) standardized the C language in 1989, thus solidifying C as a language for application and systems development. Armed with the Windows Software Development Kit (SDK) and the Microsoft C compiler, developers started developing GUI applications that took advantage of the Windows API.

The C language was procedural—it had no built-in support for the object-oriented features that are commonly used today: encapsulation, inheritance, and polymorphism. The Windows API was designed and delivered as a procedure-based interface and, hence, was perfect for the development technology of the time. However, as object-oriented extensions to C were developed and more widely accepted in a new language called C++, an object-oriented wrapper interface to the Windows API seemed a natural next step. Microsoft developed this interface as its Application Frameworks (AFX) product in 1992. This evolved into the Microsoft Foundation Classes (MFC) product that exists today.


Note:  

The Windows API is object-based. This means that you can programmatically interact with the system objects (such as windows, semaphores, and pens) through a handle and a defined interface. The actual implementation and data used by the implementation is hidden from the view of the program. This “data hiding” is called encapsulation.

The MFC Class Libraries are object-oriented. This means that in addition to the encapsulation, the interfaces (packaged in a set of C++ classes) also provide inheritance and polymorphism. Inheritance is the capability to share and extend the functionality of an existing class. Polymorphism is the capability of objects to support the same interface, but provide different implementations.



Note:  

Although the Windows API is procedural and designed to be called from procedural languages (like C), you can (and will) use the Windows API directly from your MFC applications written in C++.


The concept of device independence was a boon for both software developers and hardware manufacturers (well, at least the manufacturers that didn’t have a great amount of market share at the time). Unlike the traditional DOS programs that required specific routines for different video and print devices, programs coded for Windows could be written to a common interface and work across a wide variety of video and print devices. The result is that developers could focus more on the problem on hand, rather than the hardware used to solve the problem; and manufacturers could focus more on creating device drivers for Windows and allow a much wider variety of software that can use their devices.

Concomitant with the move to device independence, Windows GUI development forced a paradigm shift on the traditional DOS programmers. At that time, most software was written in a procedural manner: one function calling another, with the main program always being in control. The event-driven model forced programs to give up their total control and, instead, wait and respond to external events to provide their functionality to the end users.

The structure of Win16 (and now Win32) GUI programs remains the same today as it was in 1987. Figure 1.1 shows the basic structure of a Windows GUI application. Observe that each program consists of an entry point, the creation of a main window, a message loop, and the destruction of the main window. In addition, there is a function associated with the main window, called a window procedure, which contains the code that handles the system and application events (such as keyboard entry, mouse movement and clicks, timer alarms, menu selections, and pushbutton clicks).


Figure 1.1  Structure of a Windows GUI application.

The entry point of a Windows GUI program is called WinMain. Named similarly to the entry point of C programs, main, every Windows GUI application must provide a WinMain function. Unlike main, it is passed different parameters and is declared as follows in Win32 programs:

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine, int nShowCmd);

The four parameters to the WinMain function include two handles (HINSTANCE), a pointer to a string (LPSTR), and an integer (int). The instance handle represents a unique application identifier for the program’s main executable file in memory. The previous instance handle is no longer used in Win32 and is always equal to zero. The pointer to a string contains the command-line arguments passed to the program at start time. Finally, the integer contains an integer value that the program is supposed to pass to the ShowWindow function that indicates whether the main window is to appear minimized, maximized, or normal.


Note:  

A handle is simply a 16- or 32-bit value that uniquely identifies a system object to an application. By exposing only the handle of a system object, Windows can hide (encapsulate) that object’s implementation and data and provide a controllable interface to that system object. This allows Microsoft to add more functionality to existing system objects, yet still support old applications—as long as they do not change the behavior of the existing interfaces. (Occasionally some interface’s behavior does change between releases. This often is the exception rather than the rule.)

The most basic C++ classes in MFC wrap the handles to the Windows system objects: windows, device contexts, pens, and brushes, to name a few.


Because it needs to perform a great deal of initialization at program startup and it provides an object-oriented interface to the Windows API, MFC provides a WinMain function for your application. The MFC-provided WinMain calls another function, AfxWinMain, which creates and manages your CWinApp-derived application object. If you need to perform application-specific initialization, run handling, and/or termination, refer to the more detailed discussion of the CWinApp class and CWinApp::InitInstance, CWinApp::Run, and CWinApp::ExitInstance methods later in this chapter.



Without a main window, an application wouldn’t have a graphical user interface and would find it hard to respond to window-related events. Consequently, almost all Windows GUI applications create a main window as their first order of business when starting.

Using direct calls to the Win32 API, your application calls RegisterClass to register a window class (basically associating a name with a window event callback function), followed by CreateWindowEx, to create a main window (which is an instance of the registered window class). In MFC, this work is done “automagically” when Visual Studio generates the code for you. Generally, in generated code, your main frame’s application window is created in the CWinApp::InitInstance code.

The next basic feature required for every Windows application is the message loop. Typically in programs that call the Windows API directly, this is accomplished as shown following:

MSG msg;
while (GetMessage(&msg, 0, 0, 0)) {
   TranslateMessage(&msg);
   DispatchMessage(&msg);
}

This loop allows the current application thread to get system events that are queued up for windows created by that thread. The GetMessage function returns a Boolean value TRUE for all messages, except the WM_QUIT message, which allows the while loop to end, due to application termination. After the call to GetMessage returns with a message (event) for one of the thread’s windows, that thread calls TranslateMessage, to process any accelerator keys and menu hot keys, followed by DispatchMessage, to send the message to the window to which it belongs by calling that window’s registered window procedure.

What Is a Thread

You are probably intimately familiar with the concept of a process. In many operating systems, a process corresponds to an instance of executing code in the computer’s memory. Processes contain memory (usually some for code and some for data) and open resources, such as files, pipes, and other system objects. Traditionally, many operating systems had the current execution state (current machine registers, including the instruction pointer, and a stack) associated with the process. As a result, each process was treated as a single unit of execution when the operating system shared the CPU via multitasking.

Windows and many other modern operating systems allow multiple units of execution within the same process. Each unit of execution is called a thread. Each thread within a process has the same access to all the memory and resources owned by the process. However, each thread maintains its own copy of the machine registers and call stack.

Threads are often used to perform some operation “concurrently” within the process and provide a simpler and more efficient mechanism as compared to creating or “forking” a new process. (Actually, on a uniprocessor system, threads cannot literally run at the same time; however, on a multiprocessor system they actually could run simultaneously.)

Another advantage of threads is that they enable you to separate logical units of code and maximize throughput in your programs. For example, if one thread is waiting for (blocking) a file I/O request, another thread could perform some mathematical calculation, and yet another thread can handle user interface events. The end result is that the program’s overall performance can be improved because its utilization of the CPU(s) is maximized for a given amount of time.

For MFC applications, once again the message loop is automatically included as part of your application. In the case of Single Document Interface (SDI) and Multiple Document Interface (MDI) applications, this message loop is put into control through the CWinApp::Run method. (More specifically, CWinApp::Run calls the CWinThread::Run method, which is in its base class.) For dialog-based applications, the message loop is instantiated during the CWinApp::initInstance through a call to CDialog::doModal. In addition, when messages are dispatched from the thread’s message loop, they are routed through the standard MFC-owned window procedures (AfxWndProcBase and AfxWndProc) and finally mapped to a function within your window or dialog class. (This process will be explained in more detail later in this chapter.)

The final phase of a typical Windows program’s WinMain function is the destruction of the main window via a call to the DestroyWindow function. Of course, this is also part of the MFC code that your application uses. If you need to do termination processing in your application, you would override the CWinApp::ExitInstance method in your application’s CWinApp-derived class.

At this point you should have a basic understanding of how some of the more basic features of a standard Windows API application correlate to an MFC application. From this point forward, the discussions will leave the Windows API behind and deal with the features of MFC. Of course, specific Windows APIs might be mentioned from time to time, but not as a major part of any section in this book.

The MFC Class Hierarchy

So far several MFC classes have been mentioned. This section covers some of the more important classes that make up the MFC architecture.

Figure 1.2 shows the inheritance hierarchy of some of the most important application architectural classes within MFC. You might immediately observe that these classes all ultimately derive from a class named CObject.


Figure 1.2  MFC architecture class hierarchy.

CObject

The CObject class is the “mother of all MFC classes.” Well, actually not quite all MFC classes, but quite a few of them. Its primary responsibilities are to support handle runtime type information and object persistence (or serialization in MFC-speak), and to perform diagnostic output for derived objects.

Classes that are derived from the CObject class can support its features in one of four ways:

1.  (Most basic) General diagnostic support.
2.  (Dynamic) All features described thus far plus runtime type identification.
3.  (DynCreate) All features thus far plus the capability of unbound dynamic object creation. That is, the class can be constructed by code that does not know the class’s name at the time it was compiled and linked.
4.  (Serial) All features thus far plus the capability of storing and restoring instances of the object to and from a byte stream.

Diagnostic Support (All CObject-Derived Classes)

The diagnostic support within the CObject class is limited to two methods: AssertValid and Dump. Each of these methods can be called at any time from your derived class.



AssertValid

The AssertValid method allows a class to perform a sanity check on itself before continuing. It is declared as a public method in the CObject class as follows:

virtual void AssertValid() const;

If you choose to override this method, you will typically use the ASSERT macro to perform sanity checks on your object’s data. In addition, you should call your base class’s implementation of this method so it can also validate its data. Because this method is const, you cannot change your object’s state from within this method.


Caution:  

Do not depend on AssertValid working in release code. This is because the ASSERT macro is only defined when _DEBUG is defined at compilation time.


Because it is a public method, you can call it either from inside your class or from outside your class at any time.

Dump

The Dump method allows a class to put diagnostic information in the human-readable form of text and/or numbers to a stream, typically through the OutputDebugString API, which displays it in the debugger’s output window (or any other program that traps debug strings). It is declared as a public method in the CObject class as follows:

virtual void Dump(CDumpContext& dc) const;

If you override this method, first call your base class’s dump method. Next, call the insertion operator (<<) on the CDumpContext object to output information about your object. As a C++ programmer, this is no different from how you would use the cerr or cout object to output diagnostic information about your object. Finally, make sure that you do not output a trailing newline (\n) on your final line of output.


Note:  

If your CObject-derived class does not include runtime type information, CObject::dump displays only CObject as the class name. Otherwise, it properly displays the name of your derived class.


Runtime Type Information (Dynamic and DynCreate Classes)

When a class supports MFC’s Runtime Type Information (RTTI), it can respond to a request for its name and its base class. To support the Dynamic form of RTTI, simply include a DECLARE_DYNAMIC macro invocation within your class declaration and an IMPLEMENT_DYNAMIC macro invocation near your class definition.



Caution:  

Do not confuse MFC’s RTTI with the RTTI support built into the newer C++ compilers (and activated using Visual C++’s /GR switch). As of MFC 4.2, MFC’s RTTI support does not use C++’s RTTI support in any way, shape, or form.


If you have a class named CMyClass that is derived from CObject and you want your class to support RTTI, add the following to your header file:

class CMyClass : public CObject {
   DECLARE_DYNAMIC(CMyClass)
   // other class information
};

In your source file, add the following at file scope:

IMPLEMENT_DYNAMIC(CMyClass, CObject)

When your class is Dynamic, it has a static CRuntimeClass object associated within it that contains the runtime type information for the object. You can obtain a pointer to the runtime class object using the RUNTIME_CLASS macro, invoking it using the name of the class that you’d like. So, to obtain a pointer to the CRuntimeClass object associated with CMyClass (a Dynamic class), simply use the following code:

RUNTIME_CLASS(CMyClass)

An extension of the Dynamic class is a DynCreate class. When a class is DynCreate, a program can construct an object of that class simply by knowing its name at runtime as opposed to compilation time.

To support dynamic creation, your class must have a default constructor (that is, a constructor with no parameters) that creates a stable object, and you must add macro invocations for DECLARE_DYNCREATE and IMPLEMENT_DYNCREATE, as you did for dynamic objects previously.


Note:  

Because dynamic creation is a superset of the dynamic support, do not add invocations of the DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC macros to your class files.


The DynCreate macros add a method named CreateObject to your class. The implementation of this method simply calls new on your specific object and returns it as a pointer to a CObject. If you have a class named CMyClass that supports dynamic creation, you can instantiate one of these objects by using the RUNTIME_CLASS macro to get the RTTI information and call the CreateObject method on it as follows:

CMyClass *myObj = DYNAMIC_DOWNCAST(CMyClass,
                  RUNTIME_CLASS(CMyClass)->CreateObject());


Note:  

The DYNAMIC_DOWNCAST and STATIC_DOWNCAST macros improve the type safety for casting operations on MFC CObject-derived types. By using MFC’s RTTI, they can check if the cast is valid at runtime.

You can use the C++ dynamic_cast and static_cast keywords to perform typesafe casts on MFC objects as long as you compile your application with C++ RTTI turned on (Visual C++’s /GR compiler switch). MFC is already built with this switch on; however, the Visual Studio default is off.


Serialization (Serial Classes)

Serialization is the capability of an object to save its state to a byte stream and rebuild itself from that stream. If an object supports serialization, it can be saved to a file, transmitted over a socket or pipe, and later reconstituted either from that file or on the other end of the socket or pipe.

To create a CObject-derived class that supports serialization, you must add the DECLARE_SERIAL macro inside its class declaration in the header file and the IMPLEMENT_SERIAL macro in the source file containing the class’s method and data definitions, in the same manner as was demonstrated for a Dynamic class.


Note:  

Because serialization is a superset of the dynamic and dynamic creation support, do not add invocations of the DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC or the DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE macros to your class files.


When a CObject-derived class supports serialization, there are two methods that are used: IsSerializable and Serialize.

IsSerializable

The IsSerializable method allows another object to determine whether this CObject-derived class supports serialization. It is declared as a public method in the CObject class as follows:

BOOL IsSerializable() const;

Because this function is not virtual, you cannot override it in any meaningful way. The MFC CRuntimeClass object determines whether serialization is supported from its m_wSchema field. The only valid values supported within this field are 0xffff (meaning not serializable) and VERSIONABLE_SCHEMA (meaning supports standard MFC serialization).



Serialize

The Serialize method is called to actually perform the saving and restoring of the object from a serialization stream (within a CArchive object). It is declared as a public method in the CObject class as follows:

virtual void Serialize(CArchive& ar);

If your class needs to save or restore its state when being serialized, you must override this method.

Of course, if you’re implementing this method so your class can support serialization, you first need to know whether or not you need to save your object’s data to or restore your object’s data from the CArchive stream. You can use either the CArchive::IsLoading or CArchive::IsStoring methods to determine which direction you need to go.


Note:  

You are guaranteed that if CArchive::IsLoading returns TRUE, CArchive::IsStoring returns FALSE and vice versa.


Next, if the archive object is in loading mode, you can use the CArchive class’s extraction operators (>>) to retrieve data from the data stream. Likewise, if it is in storing mode, you can use its insertion operators (<<) to add data to the data stream.

Serialization “Gotchas”

The serialization protocol requires that you call your base class’s Serialize method before performing your own serialization support.

If you detect an error while handling your Serialize method, you can safely throw one of the following exceptions: CMemoryException, CFileException, or CArchiveException.

You must store your data in exactly the same order that you load it.

All MFC lists support serialization. Be very careful when serializing lists of objects. If your list contains a pointer to an object being serialized, it will attempt to “reserialize” the object and get trapped in a recursive loop. This will cause a hang followed by a stack overflow in your application.

A typical implementation of the Serialize method for the fictitious CMyClass class is shown here:

void CMyClass::Serialize(CArchive& ar)
{
   CObject::Serialize(ar);

   if (ar.IsStoring()) {
      ar << m_myData;
   } else {
      ar >> m_myData;
   }
}

Serialization can be done either explicitly or implicitly by your application through the MFC framework. When your MFC application is generated by Visual Studio, it implicitly creates a CArchive object, associates it with a CFile object, and calls the CArchive::ReadObject or CArchive::WriteObject methods in response to the File menu bar, Open, Save, and Save As commands. These methods call the Serialize method on the appropriate objects.

Likewise, you can perform your own serialization explicitly within your application. First, create a CFile or CFile-derived object (such as CSocketFile). Next, construct a CArchive object passing a pointer to your CFile object to its constructor. Finally, call either the CArchive::WriteObject or CArchive::ReadObject method to save or restore your object, respectively.

CCmdTarget

The CCmdTarget class, derived from CObject, is responsible for managing the routing of system and window events to the objects that can respond to these events. So, any class that expects to receive one of these events derives from this class and overrides the CCmdTarget::OnCmdMsg method.

Examples of classes that expect to receive system and window events are CWnd (the window class), CView (the view class), CDocument (the document class), CWinThread (the user interface thread class), and CWinApp (the application class). These classes are described in more detail later in this chapter.

The methods within the CCmdTarget class can be organized into three categories: message routing, wait cursor, and automation support.

Message Routing

In all Windows GUI applications, the application thread’s message loop processes all system and window events for that thread’s windows. MFC is no exception. However, unlike the procedural window procedure with a large switch statement, the MFC architecture maps these events to object methods for each object.

In fact, a C++ programmer would immediately think that C++ does provide a mechanism for doing just this: virtual functions. Continuing on this logic, that programmer would think that to process and dispatch the window messages, simply create a base Window object and add virtual functions for each of the possible events that could arrive. Furthermore, you would derive from the base Window object and simply override those events to which your window needs to respond.

Ah, if it were only that simple. MFC does not use the C++ virtual function mechanism for handling the various Windows events. Instead, it creates a static data structure for each CCmdTarget class called a message map. In essence, this structure maps an event to its corresponding handler in the object.

Why Doesn’t MFC Use the C++ Virtual Function Mechanism?

One explanation suggests that there is a large amount of performance overhead carrying around the virtual tables (vtables) for all the possible objects.

In analyzing this explanation, a few issues need to be considered: What does “carry” mean and how large are these vtables anyway? Certainly most MFC objects do, in fact, have vtables and, certainly, each object has an extra four bytes to store a pointer to the table. These four bytes have to be “carried” around anyway.

However, for each class of object, there is only one vtable; that is, all CWnd objects share the same vtable in memory. Also, each abstract class has a vtable that won’t ever be used. (Actually MFC now uses the _declspec(novtable) keyword to prevent these from being linked into the application.) So, the number of static vtables is less than or equal to the number of classes that exist within the executable.

In addition, each vtable has one four-byte entry for each possible event that can occur. Assuming that there are 256 events, each vtable would be approximately 1KB in size. So, if you have 100 different concrete CCmdTarget-derived classes in your application, this would add about 100KB to your application size. Certainly on a system that has only 16MB of RAM, it could eat up 2% of your physical RAM if you were running three of these programs (and all the vtables were not paged out to disk).

In the context of 16MB machines, this might be unreasonable. Today, with 128MB to 256MB standard on some machines, it might not be that bad. However, if the number of events is increased, the cost increases linearly, of course. Overall, the explanation seems plausible, but still not totally satisfying. (The term carry still seems off base, however, unless used in the context of the actual executable image.)

Another rarely espoused explanation, however, seems to indicate another possibility. C++’s virtual table mechanism does not provide any sort of forward compatibility. As a result, as Windows evolves and more events are possible, either reserved space would have to be allocated in each object’s vtable beforehand or the application would have to be recompiled for each release.

So, it would require early versions of MFC to determine up front what virtual table size is required going forward. That would be a very difficult task, except perhaps for an oracle of some sorts (no pun intended).

When MFC was originally developed, it did not have the advantage of the Component Object Model (COM) to provide a more flexible, maintainable vtable mechanism. Consequently, the designers created the message map as a slightly slower, but more memory-efficient and maintainable custom “vtable” mechanism.



The method that MFC uses for mapping system and window events to objects is called message mapping. Each class that is derived ultimately from CCmdTarget contains a message map that allows it to specify the events that it can handle and map those events to a method within the class.

To add a message map to your class, you must add an invocation of the DECLARE_MESSAGE_MAP macro within your object. For the fictitious CMyView class, this code is as follows:

class CMyView : public CView { // CView is a subclass of CCmdTarget
   DECLARE_MESSAGE_MAP()
   // other class information
};

The DECLARE_MESSAGE_MAP macro expands to declare two static members in your class, the combination of which comprises your message mappings. In addition, two methods are added; one internal (_GetBaseMessageMap), which returns the message map in the base class, and one external (GetMessageMap), which returns a pointer to the message map mappings for this class.

Next, you need to add macro invocations within your source code at file scope to actually define the message map for your class. An example of such code for the CMyView class is as follows:

BEGIN_MESSAGE_MAP(CMyView, CView)
   ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
   ON_WM_SIZE()
END_MESSAGE_MAP()

The key elements are the macro invocations for BEGIN_MESSAGE_MAP and END_MESSAGE_MAP that provide the appropriate delimiting code. In between these macros, there are macros that describe the exact events that can be handled by this class. In this case, using the ON_COMMAND macro, CMyView maps the menu item command identifier ID_FILE_OPEN to the OnFileOpen method. In addition, using the ON_WM_SIZE macro maps the WM_SIZE window event to a method named OnSize (which is actually defined in that specific macro). Because there are a large number of actual events, there are a large number of macros that you can use in a message map. For more information, please refer to header file AFXMSG_.H.

OnCmdMsg

There is one method that supports the dispatching of system and window events, OnCmdMsg. Typically, you do not need to modify the handling of this method and, hence, you can just let the event dispatching occur automatically. However, if you need to provide custom and/or dynamic routing of events, you can override this virtual function in your CCmdTarget-derived class. The declaration of this method is as follows:

virtual BOOL OnCmdMsg(UNIT nID, int nCode, void *pExtra,
                         AFX_CMDHANDLERINFO *pHandlerInfo);

Your override method must return TRUE if it handles the event, and FALSE otherwise.

Wait Cursor

The CCmdTarget class defines three methods that applications can use to change the state of the mouse pointer. These methods are BeginWaitCursor, EndWaitCursor, and RestoreWaitCursor.

BeginWaitCursor, EndWaitCursor, and RestoreWaitCursor

Use the BeginWaitCursor method to change the mouse pointer to an hourglass, thus notifying the user that the current operation might take some time. When your operation is complete, call EndWaitCursor to change the pointer back to what it was at the BeginWaitCursor call.

Use RestoreWaitCursor to change the pointer back to its original state after the pointer had been changed by some external operation, such as displaying a message box.

These methods are declared as follows in the public access section of the CCmdTarget class:

void BeginWaitCursor();
void EndWaitCursor();
void RestoreWaitCursor();

Automation Support

If your MFC application allows interaction through an IDispatch COM interface, it supports automation. The CCmdTarget class not only can dispatch system and window events to objects of its derived classes, but also it can translate automation interface methods in a similar way.

The methods within CCmdTarget that support automation are: EnableAutomation, FromIDispatch, GetIDispatch, IsResultExpected, and OnFinalRelease.

EnableAutomation

The EnableAutomation method is called from your CCmdTarget-derived class’s constructor to indicate that it contains both a dispatch and an interface map. It is declared in the public section of the CCmdTarget class as follows:

void EnableAutomation();

The dispatch and interface maps are very similar in layout to the message map that was described previously in this chapter. Like the message map, they require macro invocations in the class declaration using the DECLARE_DISPATCH_MAP and DECLARE_INTERFACE_MAP macros, respectively. The following code demonstrates these macros for the sample CMyDocument class:

class CMyDocument : public CDocument {
   DECLARE_DISPATCH_MAP()
   DECLARE_INTERFACE_MAP()
   // other class member declarations...
};

When declared in the class declaration, the dispatch map and interface maps must be defined in the source file for the class. In the case of the CMyDocument class (located in the MyApp application), these maps look as follows:

// {6C9C4209-9D58-11D2-8FAF-00400566CE21}
static const IID IID_IMyApp =
Ä{ 0x6c9c4209, 0x9d58, 0x11d2,
Ä{ 0x8f, 0xaf, 0x0, 0x40, 0x5, 0x66, 0xce, 0x21 } };

BEGIN_INTERFACE_MAP(CMyDocument, CDocument)
   INTERFACE_PART(CMyDocument, IID_IMyApp, Dispatch)
END_INTERFACE_MAP()

BEGIN_DISPATCH_MAP(CMyDocument, CDocument)
   DISP_FUNCTION(CMyDocument, “OpenFile”, OpenFile, VT_EMPTY, VTS_BSTR)
END_DISPATCH_MAP()

In this example, the COM automation interface for the MyApp application is mapped to be a dispatch interface. Additionally, one automation (dispatch) function, OpenFile, is mapped to a method of the same name.

FromIDispatch and GetIDispatch

The FromIDispatch and GetIDispatch methods allow an application to get the CCmdTarget object given an IDispatch interface pointer and vice versa. Of course, not all CCmdTarget objects contain IDispatch interfaces and, hence, might return a NULL pointer. These methods are declared in the public section of the CCmdTarget class as follows:

static CCmdTarget * FromIDispatch(LPDISPATCH lpDispatch);
LPDISPATCH GetIDispatch(BOOL bAddRef);

IsResultExpected

The IsResultExpected method returns TRUE if the automation client is waiting for a return value from the function. Otherwise, if a result is not expected, the application can ignore calculating it (particularly if it might take time to do so) and improve automation performance. This function is declared as follows in the public section of the CCmdTarget class:

BOOL IsResultExpected();

OnFinalRelease

The OnFinalRelease method is a virtual method that the CCmdTarget-derived class can choose to override to perform any sort of special processing when the last COM interface reference to or from the object is released. Otherwise, the CCmdTarget-derived object is simply deleted. This method is declared in the public section of the CCmdTarget class as shown here:

virtual void OnFinalRelease();

CWinThread

The CWinThread class is derived from the CCmdTarget class and represents a thread of execution within the MFC application. All MFC applications have at least one CWinThread object—the main application’s CWinApp object (which is derived from CWinThread). If you want to provide additional asynchronous processing within your application, you can construct and run additional CWinThread objects, as needed.

You can obtain a pointer to the current CWinThread object by calling AfxGetThread.

Within MFC, there are two different types of threads: worker threads and user interface threads.



Worker Threads

Worker threads are threads that are created to do some additional processing, but do not require any sort of system or window event processing. A worker thread would be useful to perform a time-consuming calculation or to read data from a file. By creating a worker thread, you can do additional work without interfering with the operation of the application’s user interface.

A worker thread is created using the AfxBeginThread function. In its simplest form, you simply need a callback function and a user-defined data pointer. After it has been created, a CWinThread object is returned to the calling thread, and the new worker thread starts execution. This form of AfxBeginThread is declared as follows:

CWinThread *AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,
                           int nPriority = THREAD_PRIORITY_NORMAL,
                           UINT nStackSize = 0, DWORD dwCreateFlags = 0,
                           LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );

In addition, your callback function, AFX_THREADPROC, must be declared as shown here:

typedef UINT (AFX_CDECL *AFX_THREADPROC)(LPVOID);

When the thread function is complete and wants to end itself, it simply returns or calls AFXEndThread. If you want to end the created thread from another thread, you must set up your own signaling mechanism, probably via the use of system event semaphores.

User Interface Threads

User interface threads are threads that have their own message loop and can create, interact with, and destroy user interface objects, such as modeless dialog windows that operate separately from the main application thread’s user interface.

To use user interface threads, you must first derive a class from the CWinThread class that creates a user interface element for which to handle events. You can choose to use the InitInstance and ExitInstance methods to show and hide your user interface elements. When that is done, you can choose one of two approaches to create and start the thread.

The first approach is to construct your derived CWinThread function and then call the CreateThread method to start it. This method is declared as follows in the CWinThread class:

BOOL CreateThread(DWORD dwCreateFlags = 0, UINT nStackSize = 0,
                  LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);

Using the CreateThread method is a fairly clean coding approach to creating and starting a new user interface thread.

The second approach to creating a user interface thread is very similar to the technique for creating a worker thread, except for using a different overload of the AfxBeginThread function. This overload is declared as follows:

CWinThread *AfxBeginThread(CRuntimeClass* pThreadClass,
                           int nPriority = THREAD_PRIORITY_NORMAL,
                           UINT nStackSize = 0, DWORD dwCreateFlags = 0,
                           LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);

You might note that the primary difference between the two overloads is in the initial parameter. Instead of requiring a callback function and a user-defined pointer, the user interface AFXBeginThread overload requires a pointer to the runtime class of the CWinThread-derived object that you want to instantiate. Remember to use the RUNTIME_CLASS macro to obtain a pointer to this object for a specific class.

When creating CWinThread objects, you might find it necessary to initialize some members of the CWinThread object before the thread starts executing. To do this, pass CREATE_SUSPENDED to the dwCreateFlags parameter, which creates the thread in suspended mode. After the thread has been created, set the members that you require and call the CWinThread::ResumeThread method to allow the thread to start executing.

When the thread needs to terminate, it can simply call AfxPostQuitMessage to end its message loop and set an exit code for the thread. This function is declared thus:

void AFXAPI AfxPostQuitMessage(int nExitCode);

CWinThread Methods

The methods described in the following sections are contained in the CWinThread class. Remember that because your CWinApp-derived application object has CWinThread as a base class, you can use these methods on that object.

InitInstance and ExitInstance

The InitInstance and ExitInstance virtual functions can be overridden by your application to provide pre-message loop and post-message loop initialization and termination. If you override the Run method, as you can do to create a worker thread, these virtual functions do not get called, unless you call them directly. These public methods are declared as follows:

virtual BOOL InitInstance();
virtual int ExitInstance();

The CWinThread::ExitInstance implementation automatically deletes the CWinThread object if the m_bAutoDelete member is set to TRUE. Therefore, if you override ExitInstance, call your base class’s ExitInstance to maintain this behavior. Additionally, if the call to InitInstance fails, ExitInstance is called.

Run

The Run method is where the actual thread operation occurs. The default implementation provides a message loop for the thread and continues until a WM_QUIT message is encountered. You can create a CWinThread-derived worker thread if you override the Run method to perform your worker thread processing. This method is declared as a public method as follows:

virtual int Run();

The return value from this function becomes the exit code from the thread. This value can be obtained by calling the GetExitCodeThread function in the Windows API.

SuspendThread and ResumeThread

Use the SuspendThread and ResumeThread methods to control the execution of a thread. A suspend count is maintained with each thread; thus, for each call that you make to SuspendThread, you must make an equal number of calls to ResumeThread before the thread resumes execution. These public methods are declared as shown here:

DWORD SuspendThread();
DWORD ResumeThread();

The return value from each of these methods is the suspend count for the thread upon completion of the call.

GetThreadPriority and SetThreadPriority

If you want control over the priority of a thread owned by a CWinThread object, use the GetThreadPriority and SetThreadPriority methods. The Windows thread scheduler, of course, prefers higher priority threads to lower priority ones. The priority values can be one of the following (or an integer between any two levels): THREAD_PRIORITY_IDLE, THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST, and THREAD_PRIORITY_TIME_CRITICAL.

The thread priority methods are declared as follows:

int GetThreadPriority();
void SetThreadPriority(int nPriority);


Caution:  

Do not assume that priority is an absolute value in Windows. The thread scheduler, by default and design, can randomly and temporarily boost a thread’s priority. If you need to disable this behavior, use the SetThreadPriorityBoost API.




IsIdleMessage and OnIdle

You can use the IsIdleMessage and OnIdle methods to control processing that occurs when no messages exist in the thread’s message queue.

If you think that idle message processing might be required for your application, first consider using either worker or user interface threads to do the work. If you do not want the overhead and synchronization required for threading, use the age-old technique of Windows idle message processing.

The OnIdle method is called when the thread’s message queue is empty. In your override implementation, it is suggested that you call PeekMessage with the PM_NOREMOVE flag to check for the arrival of new messages in the queue. If a message arrives, return from this function to allow the thread’s message processing to continue. The OnIdle method is declared as follows:

virtual BOOL OnIdle(LONG userCount);

By overriding the IsIdleMessage method when you also override the OnIdle method, you can prevent OnIdle from being repeatedly called in response to recurring messages. This public method is declared as shown here:

virtual BOOL IsIdleMessage(MSG *pMsg);

PreTranslateMessage

You can override the PreTranslateMessage virtual function to handle a message before it is translated from an accelerator to a command and dispatched to a window. Return TRUE from this method if you do not want the message to be processed any more. This public method is declared as follows:

virtual BOOL PreTranslateMessage(MSG *pMsg);

ProcessMessageFilter

The ProcessMessageFilter virtual function allows your application to catch messages that are trapped by the MFC message hooks. Return TRUE if you process a message passed to your implementation. In addition, call your class’s base class implementation to ensure that the message hook processing continues as designed. This method is declared as follows:

virtual BOOL ProcessMessageFilter(int hookCode, LPMSG lpMsg);

ProcessWndProcException

Override the ProcessWndProcException method to handle any MFC exceptions that have been trapped from within the Windows message processing. This method is declared as shown here:

virtual LRESULT ProcessWndProcException(CException *xcp,
Äconst MSG *pMsg);

CWinApp

The CWinApp class, derived from the CWinThread class, represents not only the program’s main thread of execution, but also the application itself. As a result, there is only one CWinApp object in any MFC application.

Typically, you derive your application class from CWinApp. In addition, you would override the InitInstance and ExitInstance virtual functions to provide your own initialization and termination support (like creating and destroying your application’s main window). If you do override these functions, remember to call your base class’s implementations, too.

Functions

There are several functions that you can call to obtain global application information. They are AfxGetApp, AfxGetInstanceHandle, AfxGetResourceHandle, and AfxGetAppName.

AfxGetApp and AfxGetAppName

Use the AfxGetApp function to obtain the pointer to the executable’s CWinApp object. Similarly, you can use AfxGetAppName to get the name of your MFC program. These functions are declared as shown here:

CWinApp * AFXAPI AfxGetApp();
LPCTSTR AFXAPI AfxGetAppName();


Note:  

The MFC application name is determined first by finding a string resource with an ID equal to AFX_IDS_APP_TITLE. If a matching string resource does not exist, the fully qualified executable filename is returned.


AfxGetInstanceHandle, AfxGetResourceHandle, and AfxSetResourceHandle

Use the AfxGetInstanceHandle to obtain a handle to the loaded executable file (EXE or DLL) within which the current code is located. Use the AfxGetResourceHandle and AfxSetResourceHandle functions to find and specify the location of the executable file that contains your bound resources. With these functions, your strings and dialogs are not required to be located in the same executable file as your code. By default, the resource handle for each module is set to be the handle of the executable file that contains that code. These functions are declared as follows:

HINSTANCE AFXAPI AfxGetInstanceHandle();
HINSTANCE AFXAPI AfxGetResourceHandle();
void AFXAPI AfxSetResourceHandle(HINSTANCE hInstResource);

If you need to determine whether or not your code is in an EXE or a DLL, use the Boolean value from the afxContextIsDLL macro.

Registry Support

Applications often use the registry to store custom parameters that assist in the usability of the software. For example, applications store items such as window positions, path names, and so on, to allow them to be customized to the user’s preferences. If your application needs to store large amounts of data, you are strongly encouraged to store this data in your own custom file, rather than the registry.

For more information about using the registry, refer to Chapter 32, “Inside the Registry.”

SetRegistryKey

Use the SetRegistryKey method to inform MFC of the location (under HKEY_CURRENT_USER\Software) at which to store all application profile data. You should use a key value that is unique and is unlikely to conflict with other applications. For example, the trademarked name of your company is an excellent choice. If you do not specify a registry key, all registry method calls will refer to a text-based .INI file.

This public method is declared as follows in the CWinApp class:

void SetRegistryKey(LPCTSTR lpszRegistryKey);
void SetRegistryKey(UINT nIDRegistryKey);


Note:  

The second form of the SetRegistryKey method takes the ID of a string resource as a parameter.



Tip:  

After you’ve called SetRegistryKey, you can use the GetAppRegistryKey and GetSectionKey methods to get the HKEY registry handle directly. When finished with the handle, you must call RegCloseKey to release it.


GetProfileDataType and WriteProfileDataType

Use the GetProfileInt, WriteProfileInt, GetProfileString, WriteProfileString, GetProfileBinary, and WriteProfileBinary methods to store or retrieve key and value pairs in or from the registry (or an .INI file, if no registry has been set via SetRegistryKey). When using these functions, use a unique name (within your specified registry key) for your application as the “section” name.

These public methods are declared as follows:

UINT GetProfileInt(LPCTSTR lpszSection, LPCTSTR lpszEntry, int nDefault);
BOOL WriteProfileInt(LPCTSTR lpszSection, LPCTSTR lpszEntry, int nValue);
CString GetProfileString(LPCTSTR lpszSection, LPCTSTR lpszEntry,
           LPCTSTR lpszDefault = NULL);
BOOL WriteProfileString(LPCTSTR lpszSection, LPCTSTR lpszEntry,
           LPCTSTR lpszValue);
BOOL GetProfileBinary(LPCTSTR lpszSection, LPCTSTR lpszEntry,
           LPBYTE* ppData, UINT* pBytes);
BOOL WriteProfileBinary(LPCTSTR lpszSection, LPCTSTR lpszEntry,
           LPBYTE pData, UINT nBytes);

Document Support

For more information about MFC document and view support, refer to Chapter 7, “The Document/View Architecture.”



AddDocTemplate

The AddDocTemplate method adds a document template to the list of documentemplates available to the application. You would typically call this method in your override of the InitInstance method. This method is declared as follows:

void AddDocTemplate(CDocTemplate *pTemplate);

GetFirstDocTemplatePosition and GetNextDocTemplate

The GetFirstDocTemplatePosition and GetNextDocTemplate methods allow you to iterate through all the document template objects (CDocTemplate) that currently are added to the application. If no document templates are available, GetFirstDocTemplatePosition returns NULL.

These public methods are declared as follows:

POSITION GetfirstDocTemplatePosition() const;
CDocTemplate *GetNextDocTemplate(POSITION& pos) const;


Note:  

Because GetNextDocTemplate updates the position value, always check that the current position is not NULL before calling GetNextDocTemplate.


OpenDocumentFile

The OpenDocumentFile method opens a file representing a document. It creates a frame and view for that document, based on matching the file extension to a registered document template. If the document is already loaded, this method activates that frame and view. This public method is declared as shown here:

virtual CDocument *OpenDocumentFile(LPCTSTR lpszFileName);

LoadStdProfileSettings and AddToRecentFileList

Use the LoadStdProfileSettings and AddToRecentFileList methods to initialize and manage your application’s recent file list. Call LoadStdProfileSettings in your InitInstance override to add the list of most recently used files to your application. Call AddToRecentFileList when you want to add another file to the recent file list. These methods are declared thus:

void LoadStdProifleSettings(UNIT nMaxMRU = _AFX_MRU_COUNT);
virtual void AddToRecentFileList(LPCTSTR lpszPathName);

EnableShellOpen, RegisterShellFileTypes, and UnregisterShellFileTypes

Call the EnableShellOpen and RegisterShellFileTypes methods in sequence to register your document templates in the system registry. By default, the RegisterShellFileTypes method iterates through your document templates and adds support for printing from the desktop shell (though the Print and PrintTo keys) and associates an icon for each template type (via the DefaultIcon key). If EnableShellOpen is called first, the RegisterShellFileTypes method will add support for opening the document from the desktop shell. Call UnregisterShellFileTypes to remove all the registered associations.

These methods are declared as follows:

void EnableShellOpen();
void RegisterShellFileTypes(BOOL bCompat=FALSE);
void UnregisterShellFileTypes();


Tip:  

Make sure that you add all your application’s document templates (via AddDocTemplate) before calling RegisterShellFileTypes.


Command-Line Parsing

Sometimes you’ll find it necessary to handle the command line passed to your application in a standard Windows application way. Use the RunEmbedded, RunAutomated, ParseCommandLine, and ParseShellCommand methods to help your application respond properly to command-line options passed to it.

RunEmbedded and RunAutomated

The RunEmbedded and RunAutomated methods search for /Embedding or /Automated (or the dash forms) in the passed command line. If found, the appropriate option is removed from the command line and TRUE is returned. Your program will receive these options if it is being launched as a server to an automation client application. Refer to Chapter 12, “MFC OLE Servers,” for more information.

These public methods are declared as follows:

BOOL RunEmbedded();
BOOL RunAutomated();

ParseCommandLine and ProcessShellCommand

The ParseCommandLine and ProcessShellCommand methods parse the command-line parameters and perform the standard application actions (such as printing, DDE, and automation) for your application. Use these methods in sequence in your application’s InitInstance override. These public methods are declared as follows:

void ParseCommandLine(CComandLineInfo& rCmdInfo);
BOOL ProcessShellCommand(CCommandLineInfo& rCmdInfo);


Tip:  

If you need to handle additional options that could be passed from the command line, derive a class from the CCommandLineInfo object and override its ParseParam method.


CWnd

The CWnd class, derived from CCmdTarget, is the most fundamental GUI object class in MFC. Instances of this class and derived classes are windows and have a system window handle (HWND) associated with them.

From the Windows point of view, a window is an object that has a registered window procedure, and consequently, can receive and handle system and window events. Most windows have a graphical representation and many respond to user input. Examples of windows include main application windows, dialogs, and controls (such as list boxes, pushbuttons, and static text fields).

NOTES on MFC Classes That Wrap System Handles

Several classes in MFC (such as CWnd, CDC, CPen, and CBrush, to name a few) provide wrappers to their respective system handles (HWND, HDC, HPEN, and HBRUSH, respectively).

When using these classes, you must understand the relationship of the lifetime of objects of that class and the lifetime of the associated system handles.

First, you can create the system handle for some classes, like CPen, in either a one- or two-step process. To create this object in one step, simply use one of its nondefault constructors that takes a style, width, and color parameter. To create a CPen object in two steps, use its default constructor and then call one of the CreatePen or CreatePenIndirect methods.

Classes typically have a Create method. However, some classes can have completely different names. For example, CFrameWnd has both a Create and a LoadFrame method to accomplish this.

Likewise, some system handle wrapper classes require a two-step process to fully construct the object. For example, if you construct a CWnd class, no system handle is created until you call the Create (or similar) method on the class. Therefore, there is a two-step process for constructing system handle wrapper objects.

First, when the MFC object has the system handle, that remains valid until the object is destroyed—at which time the handle is returned to the system.

Second, if you happen to have a system handle for which you’d like an object constructed, use the object’s Attach method to assign handle ownership to the object.

If you’d like to take ownership of the system handle from an MFC object, call its Detach method. When this call is made, the handle is disconnected completely from the object and you are responsible for releasing the handle back to the system.

Finally, if you have a system handle, you can look up and/or create a temporary instance of a wrapper class. Most system handle wrapper classes provide a FromHandle function that returns an instance of a class given a handle. If the handle does not have an object associated with it, a temporary object is created.

The CWnd class and CImageList classes also provide a FromHandlePermanent function that returns an object only if one exists for the handle. This function does not create a temporary object.



Registering New Window Classes

You can use either AfxRegisterClass or AfxRegisterWndClass to register a new window class. In either case, you can pass the registered window class’s name in a subsequent call to CWnd::Create to create an instance of that window.

AfxRegisterClass is very similar to the Win32 RegisterClass API, except that if the class is registered from within a DLL, that class is automatically unregistered when the DLL is unloaded.

The preferred window class registration function, AfxRegisterWndClass, returns a string containing the generated class name.

These functions are prototyped as follows:

BOOL AFXAPI AfxRegisterClass(WNDCLASS *lpWndClass);
LPCTSTR AFXAPI AfxRegisterWndClass(UINT nClassStyle, HCURSOR hCursor = 0,
                HBRUSH hbrBackground = 0, HICON hIcon = 0);

Obtaining an Application’s Main Window

The AfxGetMainWnd function to get the pointer to the CWnd object that represents the main window for your thread. Typically, it simply returns the m_pMainWnd member of the active CWinThread object. It is prototyped as follows:

CWnd * AFXAPI AfxGetMainWnd();


Note:  

Don’t assume that every CWinThread object in an application has the same m_pMainWnd object. When a new CWinThread object is constructed, it “inherits” the m_pMainWnd pointer from the thread that created it. However, each thread can change this pointer at any time.


Creation and Use

Like other system handle wrapper classes, CWnd provides many of the standard mechanisms for creating system handles or attaching existing ones. The methods supported are Create, CreateEx, CreateControl, FromHandle, FromHandlePermanent, Attach, and Detach.

Create and CreateEx

The Create and CreateEx methods map very closely to the Win32 APIs CreateWindow and CreateWindowEx. Like the Win32 APIs, the difference between the two is the ability to specify extended window styles in the extended version. These methods are declared as follows:

virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName,
               DWORD dwStyle, const RECT& rect, CWnd *pParentWnd,
               UINT nID, CCreateContext* pContext = NULL);
BOOL CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
ÄLPCTSTR lpszWindowName,
               DWORD dwStyle, int x, int y, int nWidth, int nHeight,
               HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam = NULL);
BOOL CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
ÄLPCTSTR lpszWindowName,
               DWORD dwStyle, const RECT& rect, CWnd *pParentWnd,               ÄUINT nID,
               LPVOID lpParam = NULL);

CreateControl

Use the CreateControl method to instantiate an ActiveX control and associate it with the CWnd object. Specify either the control’s ProgID or CLSID for the class name. The various forms of this method are declared as follows:

BOOL CreateControl(REFCLSID clsid, LPCTSTR pszWindowName, DWORD dwStyle,
   const RECT& rect, CWnd* pParentWnd, UINT nID, CFile* pPersist=NULL,
   BOOL bStorage=FALSE, BSTR bstrLicKey=NULL);
BOOL CreateControl(LPCTSTR pszClass, LPCTSTR pszWindowName, DWORD dwStyle,
   const RECT& rect, CWnd* pParentWnd, UINT nID, CFile* pPersist=NULL,
   BOOL bStorage=FALSE, BSTR bstrLicKey=NULL);
BOOL CreateControl( REFCLSID clsid, LPCTSTR pszWindowName, DWORD dwStyle,
  const POINT* ppt, const SIZE* psize, CWnd* pParentWnd, UINT nID,
  CFile* pPersist = NULL, BOOL bStorage = FALSE, BSTR bstrLicKey = NULL );

FromHandle and FromHandlePermanent

The FromHandle and FromHandlePermanent static functions look up and return an existing CWnd object based upon a passed system window handle (HWND). If a matching CWnd object does not exist, the FromHandle function creates a temporary CWnd object and attaches the specified handle to it. These functions are prototyped as shown here:

static CWND * PASCAL FromHandle(HWND hWnd);
static CWND * PASCAL FromHandlePermanent(HWND hWnd);

Attach and Detach

You can associate and unassociate a system window handle (HWND) with a CWnd object using the Attach and Detach methods. After you’ve attached the handle to the object, the object is responsible for releasing the handle back to the system, unless you detach the handle. These methods are declared as follows:

BOOL Attach(HWND hWndNew);
HWND Detach();

ExecuteDlgInit

The ExecuteDlgInit method creates a dialog window based upon the specified dialog resource. Use this function if you need to load a window from a dialog resource. It is recommended that you try to use the CDialog class first, however. This method is declared as shown here:

BOOL ExecuteDlgInit(LPCTSTR lpszResourceName);
BOOL ExecuteDlgInit(LPVOID lpResource);

PreCreateWindow

The PreCreateWindow virtual function is called before a window gets created. You can override this function if you need to modify the CREATESTRUCT of a window before it gets created. If you need to terminate the construction of a window, return FALSE. This function is declared as follows:

virtual BOOL PreCreateWindow(CREATESTRUCT& cs);

Subclassing Windows

Briefly, subclassing a window is the process of hooking its window procedure to change how that window receives and responds to specific window events. You can subclass a window to give that control a different look and feel. Subclassing windows is described in detail in Chapter 5, “Custom Control Development.” The methods that support subclassing are SubclassWindow, SubclassDlgItem, UnsubclassWindow, and PreSubclassWindow.

SubclassWindow, SubclassDlgItem, and UnsubclassWindow

The SubclassWindow and SubclassDlgItem methods provide an easy mechanism to hook into the window procedure of another window. After the window has been subclassed, you can respond to system and window messages passed to that control’s window procedure via the MFC message map mechanism. The UnsubclassWindow method returns the subclassed window to its original state.

These methods are declared as follows:

BOOL SubclassWindow(HWND hWnd);
BOOL SubclassDlgItem(UINT nID, CWnd *pParent);
HWND UnsubclassWindow();

PreSubclassWindow

Your CWnd-derived object receives the PreSubclassWindow notification prior to being subclassed. If you choose to override this virtual function, your object can perform whatever operations it needs to do prior to being subclassed. This function is declared as follows:

virtual void PreSubclassWindow();

GetSafeHwnd

Use the GetSafeHwnd method to obtain the window handle attached to the window object. It is called “safe” because it returns NULL if the this pointer of the CWnd object is equal to NULL. Additionally, this method returns NULL if the CWnd object is not attached to a window, or is attached to a NULL window handle (usually meaning the desktop window). This public method is declared as follows:

HWND GetSafeHwnd() const;

GetStyle, GetExStyle, ModifyStyle, and ModifyStyleEx

Use the GetStyle, GetExStyle, ModifyStyle, and ModifyStyleEx methods to obtain and change the style and extended style flags for a window. These public methods are declared thus:

DWORD GetStyle() const;
DWORD GetExStyle() const;
BOOL ModifyStyle(DWORD dwRemove, DWORD dwAdd, UINT nFlags = 0);
BOOL ModifyStyleEx(DWORD dwRemove, DWORD dwAdd, UINT nFlags = 0);



GetOwner and SetOwner

Use the GetOwner and SetOwner methods to obtain and modify the owner of the current window. Some controls, such as CToolBar, send notification messages to their owner windows. If the window has no owner, the parent becomes its owner. Unlike the parent/child relationships, owner/ownee relationships between windows do not limit the drawing area for the owned window. These public methods are declared as follows:

CWnd *GetOwner() const;
(e)void SetOwner(CWnd *pOwnerWnd);

ToolTip Support

ToolTips are the small yellow textual pop-ups that appear when the mouse pointer is positioned over user interface elements. You can control their appearance using the methods described in the following sections.

EnableToolTips and OnToolHitTest

Use the EnableToolTips method to turn on or off the display of ToolTips for the current window. You can override the OnToolHitTest virtual function to control the location and positioning of the ToolTip message. These methods are declared as shown here:

BOOL EnableToolTips(BOOL bEnable);
virtual int OnToolHitTest(CPoint point, TOOLINFO *pTI) const;

UpdateData

You can override the UpdateData virtual function to either initialize data into the window’s child controls or validate and save the data. If the bSaveAndValidate parameter is equal to TRUE, your function must validate and save the child controls’ data. Otherwise, you must initialize the child controls from their appropriate data source.

This public virtual function is declared as follows:

BOOL UpdateData(BOOL bSaveAndValidate = TRUE);

This method is the primary mechanism for Dialog Data Validation (DDV) and Dialog Data Exchange (DDX). For more information, refer to the section “Dialog Data Exchange” section in Chapter 2, “MFC Dialogs, Controls, and Data Interaction.”

UpdateDialogControls

Use the UpdateDialogControls method to disable child controls, menu items, and toolbar buttons if no handler exists for them. This method is declared as follows:

void UpdateDialogControls(CCmdTarget *pTarget, BOOL bDisableIfNoHandler);

CenterWindow

The CenterWindow method centers a window relative to another window. By default, it centers the window relative to its parent. If the window is owned, it centers the window relative to its owner. This public method is declared as follows:

void CenterWindow(CWnd *relativeTo = NULL);

RunModalLoop, ContinueModal, and EndModalLoop

Use the RunModalLoop, ContinueModal, and EndModalLoop methods to make the specified window modal. RunModalLoop places the window into a modal mode. The window remains modal until your application calls EndModalLoop, which causes ContinueModal to return FALSE and the modal loop to be ended. These methods are declared as follows:

int RunModalLoop(DWORD dwFlags = 0);
virtual BOOL ContinueModal();
virtual void EndModalLoop(int nResult);

Win32 Methods

As an experienced Win32 programmer, you are probably familiar with many of the functions and windows messages. Many of the methods in the CWnd class map (more or less) directly to the Win32 API functions of the same name (but without the HWND parameter, of course). Others map to some of the standard notification messages. Please consult documentation on each of these Win32 API functions and messages for more information.

Arranged by functional category, these methods are as follows:

Message Handling
SendMessage, PostMessage, IsDialogMessage, SendNotifyMessage
Window Text
SetWindowText, GetWindowText, GetWindowTextLength, SetFont, GetFont
Menu Support
GetMenu, SetMenu, DrawMenuBar, GetSystemMenu, HiliteMenuItem
Window Size and Positioning
IsIconic, IsZoomed, MoveWindow, SetWindowRgn, GetWindowRgn, SetWindowPos, ArrangeIconicWindows, BringWindowToTop, GetWindowRect, GetClientRect, GetWindowPlacement, SetWindowPlacement
Coordinate Mapping
ClientToScreen, ScreenToClient, MapWindowPoints
Painting
BeginPaint, EndPaint, GetDC, GetWindowDC, ReleaseDC, UpdateWindow, SetRedraw, GetUpdateRect, GetUpdateRgn, Invalidate, InvalidateRect, InvalidateRgn, ValidateRect, ValidateRgn, ShowWindow, IsWindowVisible, ShowOwnedPopups, GetDCEx, LockWindowUpdate, UnlockWindowUpdate, RedrawWindow, Print, PrintClient
Timer
SetTimer, KillTimer
Window Styles
IsWindowEnabled, EnableWindow, GetActiveWindow, SetActiveWindow, SetForegroundWindow, GetForegroundWindow, GetCapture, SetCapture, GetFocus, SetFocus, GetDesktopWindow
Child Window
GetDlgCtrlID, SetDlgCtrlID, GetDlgItem, CheckDlgButton, CheckRadioButton, GetCheckedRadioButton, DlgDirList, DlgDirListComboBox, DlgDirSelect, DlgDirSelectComboBox, GetDlgItemInt, GetDlgItemText, GetNextDlgGroupItem, GetNextDlgTabItem, IsDlgButtonChecked, SendDlgItemMessage, SetDlgItemInt, SetDlgItemText
Scrolling
GetScrollPos, GetScrollRange, ScrollWindow, SetScrollPos, SetScrollRange, ScrollWindowEx, GetScrollInfo, SetScrollInfo, GetScrollLimit, ShowScrollBar, EnableScrollBarCtrl, GetScrollBarCtrl
Z-order and Location
ChildWindowFromPoint, FindWindow, GetNextWindow, GetTopWindow, GetWindow, GetLastActivePopup, IsChild, GetParent, SetParent, WindowFromPoint, DestroyWindow
Alert
FlashWindow, MessageBox
Clipboard
ChangeClipboardChain, SetClipboardViewer, OpenClipboard, GetClipboardOwner, GetClipboardViewer, GetOpenClipboardWindow
Caret
CreateCaret, CreateSolidCaret, CreateGrayCaret, GetCaretPos, SetCaretPos, HideCaret, ShowCaret
Shell Interaction
DragAcceptFiles
Icon
SetIcon, GetIcon
Help
GetWindowContextHelpId, SetWindowContextHelpId

CFrameWnd

The CFrameWnd class, derived from CWnd, is a window that contains the title bar, system menu, border, minimize/maximize buttons, and an active view window. The CFrameWnd class supports the single document interface (SDI).

For multiple document interface (MDI) frame windows, use CMDIFrameWnd for the workspace frame and CMDIChildWnd for the MDI child windows. Both of the aforementioned classes are derived from CFrameWnd.

For the thinner, toolbox-style frame window, use the CMiniFrameWnd class. To support in-place editing, use the COleIPFrameWnd class.



Functions

The CFrameWnd class provides several functions that allow you to obtain the active document, view, and frame. In addition, there are functions to interact with the title bar and status bar text. These functions are described in this section.

GetActiveDocument, GetActiveView, SetActiveView, and GetActiveFrame

The GetActiveDocument, GetActiveView, and GetActiveFrame methods return a pointer to the current document, view, or frame, respectively. The SetActiveView method activates a view window. These methods are declared as follows:

virtual CDocument *GetActiveDocument();
virtual CFrameWnd *GetActiveFrame();
CView *GetActiveView() const;
void SetActiveView(CView *pViewNew, BOOL bNotify = TRUE);

GetTitle and SetTitle

The GetTitle and SetTitle methods obtain and set the text in the frame window’s title bar. These methods are declared as shown here:

CString GetTitle() const;
void SetTitle(LPCTSTR lpszTitle);

SetMessageText

The SetMessageText method sets the status bar text (in pane zero of the status bar). You can specify either a string or a string resource identifier. The variations of this method are declared as follows:

void SetMessageText(LPCTSTR lpszText);
void SetMessageText(UINT nID);

BeginModalState, EndModalState, and InModalState

The BeginModalState, EndModalState, and InModalState methods control the modality of the frame window. Use BeginModalState to enter the modal state, ExitModalState to end the modal state, and InModalState to determine your current state. These methods are declared as follows:

virtual void BeginModalState();
virtual void EndModalState();
BOOL InModalState() const;

CView

The CView class, derived from CWnd, is responsible for displaying/printing and handling all user interactions for the document attached to it. You need to derive classes from CView to present your document’s data to the user in the required ways.

When you create a CView-derived class, at the minimum, you need to override the OnDraw and OnUpdate methods.

Methods

The CView class provides methods that you can override to allow your application to respond to external events (such as redraw requests, window activation, and document changes). Additionally, there is a method to return a pointer to the document associated with the view.

OnDraw

Override the OnDraw method to draw your document view to the passed device context. This method handles drawing both to the display and a printer. If you need to have printer versus screen drawing logic within this method, call CDC::IsPrinting to determine the current state. The OnDraw method is declared as shown here:

virtual void OnDraw(CDC *pDC) = 0;

OnUpdate

Override the OnUpdate method to allow your view class to be notified when the document has changed. This method is invoked when CDocument::UpdateAllViews is called or when the view is initially attached to the document, but before it is displayed.

When you receive this notification, do not redraw the changes directly. You can cause the changes to be redrawn by calling CWnd::InvalidateRect.

The OnUpdate method is declared as follows:

virtual void OnUpdate(CView *pSender, LPARAM lHint, CObject *pHint);

GetDocument

The GetDocument method returns the document to which this view is attached. It is declared as follows:

CDocument *GetDocument() const;

OnActivateVie and OnActivateFrame

Override the OnActivateView and OnActivateFrame if your view needs to respond in some way to its activation or deactivation. These virtual functions are declared as follows:

virtual void OnActivateView(BOOL bActivate, CView *pActivateView,
                            CView *pDeactivateView);
virtual void OnActivateFrame(UINT nState, CFrameWnd *pFrameWnd);

CDocument

How Are Documents Related to Views?

With MFC, you can partition your application into a document and one or more views of that document. A document contains the data that your application works with. A view is a graphical representation of that data. Hence, all the views reference the same document. If a change is made to the document from one view, all the other views get updated with that change.

An example of a document could be a set of data that correlates the names of cities with a value representing each city’s population.

You could have several types of views of this same data. One view could be a text list that allows the user to sort itself by either city name or by population. Another view could display a map that puts the population value next to the appropriate city name. Yet another view could be the same as the first view text list, except scrolled down to a different location in the list. So, in this case, you have three total view instances of two view types.

To create a document, derive a class that represents your data from the CDocument class. To create a view type, derive a class from the CView or a CView-derived class.

For more information about MFC document and view support, refer to Chapter 7.

GetTitle and SetTitle

The GetTitle and SetTitle methods obtain and set the text in the frame window’s title bar for all the views attached to this document. These methods are declared as shown here:

const CString& GetTitle() const;
virtual void SetTitle(LPCTSTR lpszTitle);

GetPathName and SetPathName

The GetPathName and SetPathName methods obtain and set the fully qualified path associated with the document. You only need to call these methods if you are also overriding OnOpenDocument and OnSaveDocument. The GetPathName and SetPathName methods are declared as shown here:

const CString& GetPathName() const;
virtual void SetPathname(LPCTSTR lpszPathname, BOOL bAddToMRU = TRUE);

GetDocTemplate

The GetDocTemplate method returns the CDocTemplate object upon which this document is based. If this document does not have a document template associated with it, NULL is returned. This method is declared as follows:

CDocTemplate *GetDocTemplate() const;

IsModified, SetModifiedFlag, and SaveModified

The IsModified, SetModifiedFlag, and SaveModified methods are used to control the modification state within the document object. Call SetModifiedFlag whenever you make a change to your document. To determine the current modification state, call IsModified. If you want to prompt the user about saving the document modification in a way different from the default implementation, override the SaveModified method.

These methods are declared as shown here:

virtual BOOL IsModified();
virtual void SetModifiedFlag(BOOL bModified = TRUE);
virtual BOOL SaveModified();

AddView, RemoveView, and OnChangedViewList

Call the AddView and RemoveView methods to attach and detach a CView-derived object to and from this document. Each successful call to AddView and RemoveView results in a call to OnChangedViewList. The default implementation of OnChangedViewList deletes the document when the last view is detached. These public methods are declared as shown here:

void AddView(CView *pView);
void RemoveView(CView *pView);
virtual void OnChangedViewList();



GetFirstViewPosition and GetNextView

The GetFirstViewPosition and GetNextView methods enable you to iterate through all the view objects (CView) that currently are associated with the document. If no views are available, GetFirstViewPosition returns NULL.

These public methods are declared as follows:

virtual POSITION GetFirstViewPosition() const;
virtual CView *GetNextView(POSITION& pos) const;


Note:  

Because GetNextView updates the position value, always check that the current position is not NULL before calling GetNextView again.


UpdateAllViews

Use the UpdateAllViews method to have the CView::OnUpdate method to be called on one or all views associated with this document. If the pSender parameter is equal to NULL, the update is broadcast to all views associated with this document. This method is declared as shown here:

void UpdateAllViews(CView *pSender, LPARAM lHint = 0, CObject
Ä*pHint = NULL);

DeleteContents

Override the DeleteContents method to delete your document’s data before the document is destroyed or reused. Because SDI applications have only one document, it is reused. Consequently, it is important to delete your document’s data in this method. This method is declared as follows:

virtual void DeleteContents();

OnNewDocument, OnOpenDocument, OnSaveDocument, and OnCloseDocument

Override the OnNewDocument, OnOpenDocument, OnSaveDocument, and OnCloseDocument virtual functions to respond to each of the new, open, save, and close events for the document.


Tip:  

Because a single document object is used in SDI applications, you must initialize it by overriding the OnNewDocument method.


These functions are declared as follows:

virtual BOOL OnNewDocument();
virtual BOOL OnOpenDocument(LPCTSTR lpszPathName);
virtual BOOL OnSaveDocument(LPCTSTR lpszPathName);
virtual void OnCloseDocument();

ReportSaveLoadException

Override the ReportSaveLoadException virtual function if you need to provide custom error reporting when an exception is caught while saving or loading the document. This virtual function is declared as shown here:

virtual void ReportSaveLoadException(LPCTSTR lpszPathName,
                                     CException *e, BOOL bSaving,
                                     UINT nIDPDefault);

GetFile and ReleaseFile

Override the GetFile and ReleaseFile methods to provide a custom mechanism for opening and closing the document data file. These methods are declared as follows:

virtual CFile *GetFile(LPCTSTR lpszFileName, UINT nOpenFlags,
                       CFileException *pError);
virtual void ReleaseFile(CFile *pFile, BOOL bAbort);

CanCloseFrame and PreCloseFrame

The CanCloseFrame and PreCloseFrame methods give you an opportunity to handle the closing of document views contained in frame windows. Override the CanCloseFrame virtual function if you need to provide special handling when frame windows are closed. The default implementation prompts the user to save the document upon closing the last frame. Override the PreCloseFrame virtual function to provide custom handling when a frame window containing views associated with a document is closed. These methods are declared as shown here:

virtual void CanCloseFrame(CFrameWnd *pFrame);
virtual void PreCloseFrame(CFrameWnd *pFrame);

Summary

This chapter covers the fundamental architectural classes involved in almost every MFC-based application. You will incorporate all these classes either directly or indirectly in every MFC GUI application that you write.

The CObject class is the root of all other MFC classes and provides general methods that are useful to classes derived from it. The runtime type information allows each class to identify its type. The serialization support provides a mechanism for persisting the object to any byte stream. Finally, the diagnostic support provides a standard way of dumping the contents of an object and ensuring that the object is in a valid state.

The CCmdTarget, CWinThread, and CWinApp classes are a line of related classes in the MFC class hierarchy that form the basis of your GUI application. These classes allow your application to receive and process GUI events. Additionally, these classes provide the general framework for the controller part of the object-oriented model/view/controller (MVC) paradigm.

The object-oriented model and view classes are supported via MFC’s document/view architecture and the CDocument and CView classes, respectively. When you derive from each of these classes, you can separate your application’s business logic from its display logic.

Finally, the document views are displayed in GUI windows that are represented by the CFrameWnd and CWnd classes. The windowing classes are the fundamental objects that manage your GUI application and present its data to the user.